use crate::core::traits::Dot;
use crate::vector::items::Items;
use crate::vector::traits::ItemOps;
use crate::vector::vector::Vector;
use std::fmt::{Debug, Formatter};
use std::ops::{Add, Mul, Sub};
/**
矩阵
 **/
pub struct Matrix<T: Clone> {
    pub rows: i32,
    pub cols: i32,
    pub data: Vec<Items<T>>,
}

impl<T> Matrix<T>
where
    T: ItemOps,
    Items<T>: Dot<T>,
{
    pub fn new(rows: i32, cols: i32) -> Self {
        let default_data = T::default();

        Matrix {
            rows,
            cols,
            data: vec![Items(vec![default_data; cols as usize]); rows as usize],
        }
    }

    //设置行列内容
    pub fn set_data(&mut self, args: Vec<T>) {
        assert_eq!(args.len(), (self.rows * self.cols).try_into().unwrap());
        for r in 0..self.rows {
            for c in 0..self.cols {
                self.set_item(r, c, args[(r * self.cols + c) as usize].clone());
            }
        }
    }

    pub fn get_item(&self, rows: i32, cols: i32) -> T {
        self.data[rows as usize][cols as usize].clone()
    }

    pub fn set_item(&mut self, rows: i32, cols: i32, value: T) {
        self.data[rows as usize][cols as usize] = value;
    }

    pub fn get_items(&self, rows: i32) -> Items<T> {
        return self.data[rows as usize].clone();
    }

    pub fn set_items(&mut self, rows: i32, items: Items<T>) {
        self.data[rows as usize] = items;
    }
    //设置单位矩阵
    pub fn identity(&self) -> Matrix<T> {
        assert_eq!(self.rows, self.cols);

        let mut identity = Matrix::new(self.rows, self.cols);

        for r in 0..self.rows {
            for c in 0..self.cols {
                identity.set_item(r, c, T::default());
            }
            identity.set_item(r, r, T::unit());
        }

        identity
    }

    //迹（对角线的和）
    pub fn tr(&self) -> T {
        assert_eq!(self.rows, self.cols);

        let mut sum = T::default();

        for r in 0..self.rows {
            sum = sum + self.get_item(r, r)
        }

        sum
    }

    //行列转置
    pub fn transpose(&self) -> Matrix<T> {
        let mut trans = Matrix::new(self.cols, self.rows);

        for r in 0..self.rows {
            for c in 0..self.cols {
                let item = self.get_item(r, c);
                trans.set_item(c, r, item);
            }
        }

        trans
    }

    //阿达马积
    pub fn hadamard(self, rhs: Self) -> Matrix<T> {
        assert_eq!(self.rows, rhs.rows);
        assert_eq!(self.cols, rhs.cols);
        let mut matrix = self.clone();

        for r in 0..matrix.rows {
            for c in 0..matrix.cols {
                let item = matrix.get_item(r, c);
                let rhs_item = rhs.get_item(r, c);
                matrix.set_item(r, c, item * rhs_item);
            }
        }

        matrix
    }

    pub fn lu(&self) -> (Matrix<T>, Matrix<T>) {
        let mut l_matrix = self.identity();
        let mut u_matrix = self.clone();

        //正向消元
        for r in 0..u_matrix.rows {
            let mut pre_first_item = u_matrix.get_item(r, r);

            if pre_first_item == T::default() && r < u_matrix.rows - 1 {
                for c2 in 0..r {
                    let l_matrix_first = l_matrix.get_item(r, c2);
                    let l_matrix_second = l_matrix.get_item(l_matrix.rows - 1, c2);
                    l_matrix.set_item(r, c2, l_matrix_second);
                    l_matrix.set_item(l_matrix.rows - 1, c2, l_matrix_first);
                }
                u_matrix = u_matrix.clone().replace_row(r, l_matrix.rows - 1);
                pre_first_item = u_matrix.get_item(r, r);
            }

            for r1 in r + 1..u_matrix.rows {
                let first_item = u_matrix.get_item(r1, r);
                let scale = first_item / pre_first_item.clone();

                u_matrix.elimination_with_row(r, r1, scale.clone());
                l_matrix.set_item(r1, r, scale);
            }
        }

        (l_matrix, u_matrix)
    }
    //逆
    pub fn reciprocal(&self) -> Matrix<T> {
        let identity_matrix = self.identity();

        let mut augmented_matrix = self.combine(identity_matrix);
        //正向消元
        for r in 0..augmented_matrix.rows {
            let main_item = augmented_matrix.get_item(r, r);
            let main_scale = T::unit() / main_item;
            augmented_matrix.scale_with_row(r, main_scale);

            let pre_first_item = augmented_matrix.get_item(r, r);

            for r1 in r + 1..augmented_matrix.rows {
                let scale = augmented_matrix.get_item(r1, r) / pre_first_item.clone();
                augmented_matrix.elimination_with_row(r, r1, scale);
            }
        }
        //逆向消元
        for r_reverst in 0..augmented_matrix.rows {
            let r = augmented_matrix.rows - 1 - r_reverst;
            let main_item = augmented_matrix.get_item(r, r);
            let main_scale = T::unit() / main_item;

            augmented_matrix.scale_with_row(r, main_scale);

            let pre_first_item = augmented_matrix.get_item(r, r);

            for r1 in 0..r {
                let scale = augmented_matrix.get_item(r1, r) / pre_first_item.clone();
                augmented_matrix.elimination_with_row(r, r1, scale);
            }
        }
        //拆除
        let mut matrix = Matrix {
            rows: self.rows,
            cols: self.cols,
            data: vec![Items(vec![T::default(); self.cols as usize]); self.rows as usize],
        };

        for r in 0..self.rows {
            for c1 in 0..self.cols {
                let item = augmented_matrix.get_item(r, c1 + self.cols);
                matrix.set_item(r, c1, item);
            }
        }

        matrix
    }

    pub fn combine(&self, other: Self) -> Matrix<T> {
        let mut matrix = Matrix::new(self.rows, self.cols * 2);

        for r in 0..self.rows {
            for c0 in 0..self.cols {
                let item = self.get_item(r, c0);
                matrix.set_item(r, c0, item);
            }

            for c1 in 0..other.cols {
                let item = other.get_item(r, c1);
                matrix.set_item(r, c1 + self.cols, item);
            }
        }

        matrix
    }

    pub fn replace_row(self, row1: i32, row2: i32) -> Matrix<T> {
        let mut identity = self.identity();

        let temp_row1_col1 = identity.get_item(row1, row1);
        let temp_row1_col2 = identity.get_item(row1, row2);
        let temp_row2_col1 = identity.get_item(row2, row1);
        let temp_row2_col2 = identity.get_item(row2, row2);

        identity.set_item(row1, row1, temp_row1_col2);
        identity.set_item(row1, row2, temp_row1_col1);
        identity.set_item(row2, row1, temp_row2_col2);
        identity.set_item(row2, row2, temp_row2_col1);

        identity * self
    }

    pub fn replace_col(self, col1: i32, col2: i32) -> Matrix<T> {
        let mut identity = self.identity();

        let temp_row1_col1 = identity.get_item(col1, col1);
        let temp_row1_col2 = identity.get_item(col1, col2);
        let temp_row2_col1 = identity.get_item(col2, col1);
        let temp_row2_col2 = identity.get_item(col2, col2);

        identity.set_item(col1, col1, temp_row1_col2);
        identity.set_item(col1, col2, temp_row1_col1);
        identity.set_item(col2, col1, temp_row2_col2);
        identity.set_item(col2, col2, temp_row2_col1);

        self * identity
    }

    fn elimination_with_row(&mut self, pre_row: i32, row: i32, scale: T) {
        let pre_items = self.data[pre_row as usize].clone();
        let items = self.data[row as usize].clone();
        self.set_items(row, items - (pre_items * scale));
    }

    fn scale_with_row(&mut self, row: i32, scale: T) {
        let items = self.data[row as usize].clone();
        self.set_items(row, items * scale);
    }
}

impl<T: Clone> Clone for Matrix<T> {
    fn clone(&self) -> Self {
        Self {
            rows: self.rows,
            cols: self.cols,
            data: self.data.clone(),
        }
    }
}

impl<T> Add for Matrix<T>
where
    T: ItemOps + Add,
    Vec<T>: FromIterator<<T as Add>::Output>,
    Items<T>: Dot<T>,
{
    type Output = Matrix<T>;

    fn add(self, rhs: Self) -> Self::Output {
        assert_eq!(self.rows, rhs.rows);
        assert_eq!(self.cols, rhs.cols);
        let mut matrix = self.clone();

        for r in 0..matrix.rows {
            let items = matrix.get_items(r);
            let rhs_items = rhs.get_items(r);
            matrix.set_items(r, items + rhs_items);
        }

        matrix
    }
}

impl<T> Sub for Matrix<T>
where
    T: ItemOps,
    Vec<T>: FromIterator<<T as Sub>::Output>,
    Items<T>: Dot<T>,
{
    type Output = Matrix<T>;

    fn sub(self, rhs: Self) -> Self::Output {
        assert_eq!(self.rows, rhs.rows);
        assert_eq!(self.cols, rhs.cols);
        let mut matrix = self.clone();

        for r in 0..matrix.rows {
            let items = matrix.get_items(r);
            let rhs_items = rhs.get_items(r);
            matrix.set_items(r, items - rhs_items);
        }

        matrix
    }
}

impl<T> Mul<Matrix<T>> for f64
where
    T: ItemOps,
    Items<T>: Mul<f64, Output = Items<T>>,
    Items<T>: Dot<T>,
{
    type Output = Matrix<T>;

    fn mul(self, rhs: Matrix<T>) -> Self::Output {
        let mut matrix = rhs.clone();

        for r in 0..matrix.rows {
            let items = matrix.get_items(r);
            matrix.set_items(r, items * self);
        }

        matrix
    }
}

impl<T> Mul<Vector<T>> for Matrix<T>
where
    T: ItemOps,
    Items<T>: Dot<T>,
{
    type Output = Vector<T>;

    fn mul(self, rhs: Vector<T>) -> Vector<T> {
        assert_eq!(self.cols, rhs.len() as i32);
        let mut vector = Vector::new(vec![T::default(); self.rows as usize]);
        for r in 0..self.rows {
            let line_vector = Vector::new_from_items(self.data[r as usize].clone());
            vector[r as usize] = line_vector.dot(rhs.clone());
        }

        vector
    }
}

impl<T> Mul for Matrix<T>
where
    T: ItemOps,
    Items<T>: Dot<T>,
{
    type Output = Matrix<T>;

    fn mul(self, rhs: Self) -> Self::Output {
        assert_eq!(self.cols, rhs.rows);
        let mut matrix = Matrix {
            rows: self.rows,
            cols: rhs.cols,
            data: vec![Items(vec![T::default(); rhs.cols as usize]); self.rows as usize],
        };

        for r in 0..self.rows {
            let row_items = self.data[r as usize].clone();

            for c in 0..rhs.cols {
                let col_items = Items(rhs.data.iter().map(|val| val[c as usize].clone()).collect());

                matrix.set_item(r, c, row_items.dot(col_items))
            }
        }

        matrix
    }
}

impl<T: Debug + Clone> Debug for Matrix<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "\n")?;
        for row in 0..self.rows {
            let item = self.data[row as usize].clone();
            write!(f, "{:?}\n", item)?;
        }
        Ok(())
    }
}
